﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TravelAgencyDAL.DAL;
using TravelAgencyDAL;

namespace SearchOperations
{
    public class DijkstraSearching
    {
        private static ProbaTravelDataContext cont;

        static DijkstraSearching()
        {
            cont = new ProbaTravelDataContext();
        }

        private static Dictionary<long, RoadPoint> GetLessPrice(long startID, long endID)
        {
            Dictionary<long, RoadPoint> dict = new Dictionary<long, RoadPoint>();
            decimal tempPrice;
            int visitedCounter = 0;
            int settlementCount;
            bool allVisted = false;


            foreach (long settID in GetAllSettlementID())
            {
                dict.Add(settID, new RoadPoint());

            }

            settlementCount = dict.Count;
            dict[startID].LastSettlementID.Add((long)0);
            dict[startID].Price = (decimal)0;

            while (!allVisted)
            {
                long priceMinSettID = (from kvp in dict
                                       where kvp.Value.Visited == false
                                       orderby kvp.Value.Price
                                       select kvp.Key).First();

                if (priceMinSettID == endID)
                {
                    break;
                }

                if (dict[priceMinSettID].Price < decimal.MaxValue)
                {
                    dict[priceMinSettID].Visited = true;
                    visitedCounter++;
                    List<long> nextSettlementList = GetAllNextSettlementsID(priceMinSettID);

                    foreach (long l in nextSettlementList)
                    {
                        tempPrice = GetPriceSettlements(priceMinSettID, l);

                        if (dict[priceMinSettID].Price + tempPrice < dict[l].Price)
                        {
                            dict[l].Price = dict[priceMinSettID].Price + tempPrice;
                            dict[l].LastSettlementID.Clear();
                            dict[l].LastSettlementID.Add(priceMinSettID);
                        }
                        else if (dict[priceMinSettID].Price + tempPrice == dict[l].Price)
                        {
                            dict[l].LastSettlementID.Add(priceMinSettID);
                        }
                    }

                    allVisted = (visitedCounter == settlementCount);
                }
                else
                {
                    break;
                }
            }

            return dict;
        }

        private static List<ComplexDestination> GetTravelPoints(Dictionary<long, RoadPoint> dict,
            long start, long end, bool isPrice)
        {
            List<ComplexDestination> complList = new List<ComplexDestination>();
            List<bool> isStart = new List<bool>();
            bool isReachable;

            if (isPrice)
            {
                isReachable = (dict[end].Price != decimal.MaxValue);
            }
            else
            {
                isReachable = (dict[end].Time != TimeSpan.MaxValue);
            }

            if (isReachable)
            {
                foreach (long l in dict[end].LastSettlementID)
                {
                    ComplexDestination compl = new ComplexDestination();
                    compl.settlementID.Add(end);
                    compl.settlementID.Add(l);

                    if (compl.settlementID.Last() == start)
                    {
                        complList.Add(compl);
                        isStart.Add(true);
                    }
                    else
                    {
                        complList.Add(compl);
                        isStart.Add(false);
                    }
                }

                while (isStart.Contains(false))
                {
                    for (int j = 0; j < complList.Count; j++)
                    {
                        if (isStart.ElementAt(j) == false)
                        {
                            if (dict[complList.ElementAt(j).settlementID.Last()].LastSettlementID.Count == 1)
                            {
                                complList.ElementAt(j).settlementID.Add(
                                    dict[complList.ElementAt(j).settlementID.Last()].LastSettlementID.First());
                                if (complList.ElementAt(j).settlementID.Last() == start)
                                {
                                    isStart.RemoveAt(j);
                                    isStart.Insert(j, true);
                                }
                            }
                            else
                            {
                                int length = complList.Count;
                                int cloneCount = dict[complList.ElementAt(j).settlementID.Last()].LastSettlementID.Count - 1;
                                int lPointer = 1;

                                for (int i = 0; i < cloneCount; i++)
                                {
                                    complList.Add(CloneComplex(complList.ElementAt(j)));
                                    isStart.Add(false);
                                }

                                complList.ElementAt(j).settlementID.Add
                                    (dict[complList.ElementAt(j).settlementID.Last()].LastSettlementID.First());
                                if (complList.ElementAt(j).settlementID.Last() == start)
                                {
                                    isStart.RemoveAt(j);
                                    isStart.Insert(j, true);
                                }

                                for (int i = length; i < complList.Count; i++)
                                {
                                    complList.ElementAt(i).settlementID.Add(
                                        dict[complList.ElementAt(j).settlementID.Last()].LastSettlementID.ElementAt(lPointer));
                                    if (complList.ElementAt(i).settlementID.Last() == start)
                                    {
                                        isStart.RemoveAt(i);
                                        isStart.Insert(i, true);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            foreach (ComplexDestination compl in complList)
            {
                compl.settlementID.Reverse();
            }

            return complList;
        }

        private static Dictionary<DateTime, Dictionary<long, RoadPoint>> GetLessTime(long startID, long endID,
            DateTime date)
        {
            Dictionary<DateTime, Dictionary<long, RoadPoint>> minDictList =
                new Dictionary<DateTime, Dictionary<long, RoadPoint>>();
            List<RoadSegment> allRoadSegments = new List<RoadSegment>();
            List<TimedRoadSegment> allTimedRoadSegments = new List<TimedRoadSegment>();
            Dictionary<DateTime, long> timedStartVariants = new Dictionary<DateTime, long>();
            TimeSpan tempTime;
            int visitedCounter;
            int settlementCount;
            bool allVisted = false;

            List<long> allNext = DijkstraSearching.GetAllNextSettlementsID(startID);

            foreach (long l in allNext)
            {
                List<RoadSegment> rsList = GetRoadSegments(startID, l);

                foreach (RoadSegment rs in rsList)
                {
                    allRoadSegments.Add(rs);
                }
            }

            foreach (RoadSegment rs in allRoadSegments)
            {
                List<DateTime> startHours = GetStartHours(rs, date);

                foreach (DateTime d in startHours)
                {
                    timedStartVariants.Add(d, WayToSettlementID(rs.EndWayStationID));
                }
            }

            foreach (KeyValuePair<DateTime, long> keyVP in timedStartVariants)
            {
                Dictionary<long, RoadPoint> dict = new Dictionary<long, RoadPoint>();

                foreach (long settID in GetAllSettlementID())
                {
                    dict.Add(settID, new RoadPoint());

                }

                visitedCounter = 1;
                settlementCount = dict.Count;
                dict[startID].Visited = true;
                dict[startID].LastSettlementID.Add((long)0);
                dict[startID].Time = TimeSpan.MinValue;

                dict[keyVP.Value].Time = GetTimeSettlement(startID, keyVP.Value);
                dict[keyVP.Value].LastSettlementID.Add(startID);

                while (!allVisted)
                {
                    long timeMinSettID = (from kvp in dict
                                          where kvp.Value.Visited == false
                                          orderby kvp.Value.Time
                                          select kvp.Key).First();

                    if (timeMinSettID == endID)
                    {
                        break;
                    }

                    if (dict[timeMinSettID].Time < TimeSpan.MaxValue)
                    {
                        dict[timeMinSettID].Visited = true;
                        visitedCounter++;

                        List<long> nextSettlementList = GetAllNextSettlementsID(timeMinSettID);

                        foreach (long l in nextSettlementList)
                        {
                            tempTime = GetTimeSettlement(timeMinSettID, l);
                            DateTime tempStartHour = GetNextStartHour(keyVP.Key + dict[timeMinSettID].Time,
                                timeMinSettID, l);

                            if (tempStartHour - keyVP.Key + tempTime < dict[l].Time)
                            {
                                dict[l].Time = tempStartHour - keyVP.Key + tempTime;
                                dict[l].LastSettlementID.Clear();
                                dict[l].LastSettlementID.Add(timeMinSettID);
                            }
                            else if (tempStartHour - keyVP.Key + tempTime == dict[l].Time)
                            {
                                dict[l].LastSettlementID.Add(timeMinSettID);
                            }
                        }

                        allVisted = (visitedCounter == settlementCount);
                    }
                    else
                    {
                        break;
                    }
                }

                if (minDictList.Count == 0)
                {
                    minDictList.Add(keyVP.Key, dict);
                }
                else
                {
                    if (dict[endID].Time < minDictList.Last().Value.Last().Value.Time)
                    {
                        minDictList.Clear();
                        minDictList.Add(keyVP.Key, dict);
                    }
                    else if (dict[endID].Time == minDictList.Last().Value.Last().Value.Time)
                    {
                        minDictList.Add(keyVP.Key, dict);
                    }
                }
            }
            return minDictList;
        }

        private static List<long> GetCommonDest(long start, long end)
        {
            List<long> startDest = DijkstraSearching.GetAllDestination(start);
            List<long> endDest = DijkstraSearching.GetAllDestination(end);
            List<long> commonDest =startDest.Intersect(endDest).ToList();

            return commonDest;
        }

        private static long WayToSettlementID(long wayStationID)
        {
            long sett = (from s in cont.Settlements
                         join w in cont.WayStations
                         on s.SettlementID equals w.SettlementID
                         where w.WayStationID == wayStationID
                         select s.SettlementID).First();

            return sett;
        }

        public static List<Route> GetAllRoutes(DateTime date, long startID, long endID, bool isPrice)
        {
            List<Route> routeList = new List<Route>();
            //List<ComplexDestination> searchedList = GetFullList(startID, endID);
            //List<TravelVariant> variantList = GetVariants(searchedList, maxChanges);
            Dictionary<DateTime, List<ComplexDestination>> timedComplex =
                new Dictionary<DateTime, List<ComplexDestination>>();
            Dictionary<DateTime, List<TravelVariant>> timedVariants =
                new Dictionary<DateTime, List<TravelVariant>>();

            List<ComplexDestination> complList = new List<ComplexDestination>();
            List<TravelVariant> travelList = new List<TravelVariant>();

            if (isPrice)
            {
                complList = GetTravelPoints(GetLessPrice(startID, endID), startID, endID, true);
                travelList = GetVariants(complList);

                foreach (TravelVariant tv in travelList)
                {
                    List<RoadSegment> rsList = GetRoadSegments(tv.SettlementID.ElementAt(0),
                        tv.SettlementID.ElementAt(1));

                    foreach (RoadSegment rs in rsList)
                    {
                        List<TravelVariant> tvl = new List<TravelVariant>();
                        tvl.Add(tv);
                        foreach (DateTime dt in GetStartHours(rs, date))
                        {
                            timedVariants.Add(dt, tvl);
                        }
                    }
                }
            }
            else
            {
                Dictionary<DateTime, Dictionary<long, RoadPoint>> timeDict =
                    GetLessTime(startID, endID, date);

                foreach (KeyValuePair<DateTime, Dictionary<long, RoadPoint>> kvp in timeDict)
                {
                    timedComplex.Add(kvp.Key, GetTravelPoints(kvp.Value, startID, endID, false));
                }

                foreach (KeyValuePair<DateTime, List<ComplexDestination>> kvp in timedComplex)
                {
                    timedVariants.Add(kvp.Key, GetVariants(kvp.Value));
                }
            }

            //Is it necessary?
            //if (timedVariants.Count == 0)
            //{
            //    return routeList;
            //}

            foreach (KeyValuePair<DateTime, List<TravelVariant>> kvp in timedVariants)
            {
                foreach (TravelVariant tVariant in kvp.Value)
                {
                    List<RoadSegment> rsList = GetRoadSegments(tVariant.SettlementID.ElementAt(0),
                        tVariant.SettlementID.ElementAt(1));

                    foreach (RoadSegment rSegment in rsList)
                    {
                        TimedRoadSegment trSegment = new TimedRoadSegment(rSegment, kvp.Key);
                        Route route = new Route();

                        SegmentInfo sInfo = new SegmentInfo();
                        decimal price;

                        sInfo.DestinationName = string.Format("Destination: " + GetDestinationName(rSegment.StartWayStationID));
                        sInfo.StartHour = kvp.Key;
                        sInfo.RoadSegments = GetSegmentNames(rSegment);
                        sInfo.EndHour = kvp.Key.AddMinutes(GetTimeForRoadSegment(rSegment.StartWayStationID,
                            rSegment.EndWayStationID, false)); ;
                        route.DisplayInfo.Add(sInfo);
                        price = GetPriceForRoadSegment(rSegment);
                        route.Price = price;

                        StringBuilder wayStationInfo = new StringBuilder();
                        wayStationInfo.Append(kvp.Key + "," + rSegment.StartWayStationID + "," + rSegment.EndWayStationID + ";");

                        for (int i = 1; i < tVariant.SettlementID.Count - 1; i++)
                        {
                            TimedRoadSegment nextSegment = GetNextTimedRoadSegment(trSegment, tVariant.SettlementID.ElementAt(i),
                                tVariant.SettlementID.ElementAt(i + 1));
                            SegmentInfo sNextInfo = new SegmentInfo();

                            sNextInfo.DestinationName = string.Format("Destination: " +
                                GetDestinationName(nextSegment.RoadSegment.StartWayStationID));
                            sNextInfo.StartHour = nextSegment.StartHour;
                            sNextInfo.RoadSegments = GetSegmentNames(nextSegment.RoadSegment);
                            sNextInfo.EndHour = nextSegment.StartHour.AddMinutes(GetTimeForRoadSegment
                                (nextSegment.RoadSegment.StartWayStationID, nextSegment.RoadSegment.EndWayStationID, false));

                            route.DisplayInfo.Add(sNextInfo);
                            route.Price += GetPriceForRoadSegment(nextSegment.RoadSegment);
                            wayStationInfo.Append(nextSegment.StartHour + "," + nextSegment.RoadSegment.StartWayStationID + "," +
                                nextSegment.RoadSegment.EndWayStationID + ";");

                            trSegment = nextSegment;
                        }

                        TimeSpan ts = route.DisplayInfo.Last().EndHour - route.DisplayInfo.First().StartHour;

                        string hours = (ts.Days * 24 + ts.Hours).ToString();
                        string minutes = ts.Minutes.ToString();

                        if (minutes.Equals("0") == true)
                        {
                            minutes = "00";
                        }
                        route.TotalTime = string.Format("{0},{1}", hours, minutes);

                        route.WayStationInfo = wayStationInfo.ToString();

                        routeList.Add(route);

                    }
                }
            }

            return routeList;
        }

        #region HelpMethods

        private static List<TravelVariant> GetVariants(List<ComplexDestination> searchedList)
        {
            List<TravelVariant> result = new List<TravelVariant>();

            foreach (ComplexDestination compl in searchedList)
            {
                List<long> dest = GetAllDestination(compl.settlementID.First());
                List<long> nextDest = GetAllDestination(compl.settlementID.ElementAt(1));
                List<long> intersectLast = dest.Intersect(nextDest).ToList();
                List<long> intersectNew = Clone(intersectLast);

                int counterLast = 1;
                TravelVariant travel = new TravelVariant();
                travel.SettlementID.Add(compl.settlementID.First());

                while (counterLast < compl.settlementID.Count)
                {
                    while (intersectNew.Count > 0)
                    {
                        counterLast++;
                        intersectLast = Clone(intersectNew);
                        if (counterLast == compl.settlementID.Count) { break; }
                        nextDest = GetAllDestination(compl.settlementID.ElementAt(counterLast));
                        intersectNew = intersectLast.Intersect(nextDest).ToList();
                    }
                    counterLast--;
                    if (counterLast == compl.settlementID.Count - 1)
                    {
                        travel.SettlementID.Add(compl.settlementID.ElementAt(counterLast));
                        break;
                    }
                    travel.SettlementID.Add(compl.settlementID.ElementAt(counterLast));
                    dest = GetAllDestination(compl.settlementID.ElementAt(counterLast));
                    nextDest = GetAllDestination(compl.settlementID.ElementAt(counterLast + 1));
                    intersectLast = dest.Intersect(nextDest).ToList();
                    intersectNew = Clone(intersectLast);
                    counterLast++;
                }
                result.Add(travel);
            }

            return result;
        }

        private static List<long> Clone(List<long> list)
        {
            List<long> newList = new List<long>();

            foreach (long l in list)
            {
                newList.Add(l);
            }

            return newList;
        }

        public static List<long> GetAllDestination(long settlementID)
        {
            var destinationListID = from d in cont.Destinations
                                    join w in cont.WayStations
                                    on d.DestinationID equals w.DestinationID
                                    where w.SettlementID == settlementID
                                    select d.DestinationID;

            return destinationListID.ToList();
        }

        private static List<RoadSegment> GetRoadSegments(long settlementStartID, long settlementEndID)
        {
            List<RoadSegment> roadSegmentList = new List<RoadSegment>();

            var destStart = from d in cont.Destinations
                            join w in cont.WayStations
                            on d.DestinationID equals w.DestinationID
                            where w.SettlementID == settlementStartID
                            select w.DestinationID;

            var destEnd = from d in cont.Destinations
                          join w in cont.WayStations
                          on d.DestinationID equals w.DestinationID
                          where w.SettlementID == settlementEndID
                          select w.DestinationID;

            foreach (long l in destStart.ToList().Intersect(destEnd).ToList())
            {
                RoadSegment roadSegment = new RoadSegment();
                roadSegment.StartWayStationID = GetWayStationID(l, settlementStartID);
                roadSegment.EndWayStationID = GetWayStationID(l, settlementEndID);
                roadSegmentList.Add(roadSegment);
            }
            return roadSegmentList;
        }

        private static string GetDestinationName(long wayStationID)
        {
            string destinationName = (from d in cont.Destinations
                                      join w in cont.WayStations
                                      on d.DestinationID equals w.DestinationID
                                      where w.WayStationID == wayStationID
                                      select d.DestistionName).First();

            return destinationName;
        }

        private static string GetSegmentNames(RoadSegment rSegment)
        {
            StringBuilder wayStationNames = new StringBuilder();

            int startNumber = GetCurrentOrderNumber(rSegment.StartWayStationID);
            int endNumber = GetCurrentOrderNumber(rSegment.EndWayStationID);
            long destinationId = GetDestinationIDForWayStation(rSegment.EndWayStationID);

            var names = from d in cont.Destinations
                        join w in cont.WayStations
                        on d.DestinationID equals w.DestinationID
                        join s in cont.Settlements
                        on w.SettlementID equals s.SettlementID
                        where w.WayStationOrderNumber >= startNumber & w.WayStationOrderNumber <= endNumber
                        & w.DestinationID == destinationId
                        orderby w.WayStationOrderNumber
                        select s.SettlementName;

            foreach (string str in names.ToList())
            {
                wayStationNames.Append(str);
                wayStationNames.Append('-');
            }

            wayStationNames.Remove(wayStationNames.Length - 1, 1);

            return wayStationNames.ToString();
        }

        private static int GetTimeForRoadSegment(long startWayStationID,
            long endWayStationID, bool lastStayInclude)
        {
            int result;
            if (startWayStationID == endWayStationID)
            {
                result = 0;
            }
            else
            {
                long destinationID = (from w in cont.WayStations
                                      where w.WayStationID == startWayStationID
                                      select w.DestinationID).First();

                int startOrderNumber = (from w in cont.WayStations
                                        where w.WayStationID == startWayStationID
                                        select w.WayStationOrderNumber).First();

                int endOrderNumber = (from w in cont.WayStations
                                      where w.WayStationID == endWayStationID
                                      select w.WayStationOrderNumber).First();

                var timeTravelList = from w in cont.WayStations
                                     where w.DestinationID == destinationID & w.WayStationOrderNumber > startOrderNumber
                                     & w.WayStationOrderNumber <= endOrderNumber
                                     select w.WayStationTimeTo;

                int timeTravel;

                timeTravel = (from w in cont.WayStations
                              where w.DestinationID == destinationID & w.WayStationOrderNumber > startOrderNumber
                              & w.WayStationOrderNumber <= endOrderNumber
                              select w.WayStationTimeTo).Sum();

                int timeStay;

                if (lastStayInclude)
                {
                    var timeStayList = from w in cont.WayStations
                                       where w.DestinationID == destinationID & w.WayStationOrderNumber > startOrderNumber
                                       & w.WayStationOrderNumber <= endOrderNumber
                                       select w.WayStationStay;

                    timeStay = (from w in cont.WayStations
                                where w.DestinationID == destinationID & w.WayStationOrderNumber > startOrderNumber
                                & w.WayStationOrderNumber <= endOrderNumber
                                select w.WayStationStay).Sum();
                }
                else
                {
                    var timeStayList = from w in cont.WayStations
                                       where w.DestinationID == destinationID & w.WayStationOrderNumber > startOrderNumber
                                       & w.WayStationOrderNumber < endOrderNumber
                                       select w.WayStationStay;

                    if (timeStayList.ToList().Count == 0)
                    {
                        timeStay = 0;
                    }
                    else
                    {
                        timeStay = (from w in cont.WayStations
                                    where w.DestinationID == destinationID & w.WayStationOrderNumber > startOrderNumber
                                    & w.WayStationOrderNumber < endOrderNumber
                                    select w.WayStationStay).Sum();
                    }
                }
                result = timeStay + timeTravel;
            }

            return result;
        }

        public static decimal GetPriceForRoadSegment(RoadSegment rSegment)
        {
            int startNumber = GetCurrentOrderNumber(rSegment.StartWayStationID);
            int endNumber = GetCurrentOrderNumber(rSegment.EndWayStationID);
            long destinationID = GetDestinationIDForWayStation(rSegment.EndWayStationID);

            var price = (from d in cont.Destinations
                         join w in cont.WayStations
                         on d.DestinationID equals w.DestinationID
                         where w.WayStationOrderNumber > startNumber & w.WayStationOrderNumber <= endNumber
                         & w.DestinationID == destinationID
                         select w.WayStationPriceTo).Sum();

            return price;
        }

        private static TimedRoadSegment GetNextTimedRoadSegment(TimedRoadSegment trSegment, long startSettlementID,
            long endSettlementID)
        {
            List<RoadSegment> rsList = GetRoadSegments(startSettlementID, endSettlementID);
            List<DateTime> startHours = new List<DateTime>();
            DateTime temp;
            DateTime min;
            RoadSegment searchedSegment = new RoadSegment();

            int timeForRoadSegment = GetTimeForRoadSegment(trSegment.RoadSegment.StartWayStationID,
                trSegment.RoadSegment.EndWayStationID, false);

            DateTime arriveHour = trSegment.StartHour.AddMinutes(timeForRoadSegment);

            temp = arriveHour;
            min = arriveHour.AddDays(8);

            foreach (RoadSegment rs in rsList)
            {
                while (startHours.Count == 0)
                {
                    foreach (DateTime d in GetStartHours(rs, temp))
                    {
                        if (d > arriveHour)
                        {
                            startHours.Add(d);
                            break;
                        }
                    }
                    temp = temp.AddDays(1);
                }

                foreach (DateTime d in startHours)
                {
                    if (d < min)
                    {
                        min = d;
                        searchedSegment = rs;
                    }
                }
                startHours.Clear();
                temp = arriveHour;
            }

            return new TimedRoadSegment(searchedSegment, min);
        }

        private static long GetWayStationID(long destinationID, long settlementID)
        {
            var wayStationID = (from d in cont.Destinations
                                join w in cont.WayStations
                                on d.DestinationID equals w.DestinationID
                                where d.DestinationID == destinationID & w.SettlementID == settlementID
                                select w.WayStationID).First();

            return wayStationID;
        }

        public static int GetCurrentOrderNumber(long wayStationID)
        {
            int orderNumber = (from w in cont.WayStations
                               where w.WayStationID == wayStationID
                               select w.WayStationOrderNumber).First();

            return orderNumber;
        }

        public static long GetDestinationIDForWayStation(long wayStationID)
        {
            var destinationID = (from d in cont.Destinations
                                 join w in cont.WayStations
                                 on d.DestinationID equals w.DestinationID
                                 where w.WayStationID == wayStationID
                                 select d.DestinationID).First();

            return destinationID;
        }

        public static List<DateTime> GetStartHours(RoadSegment rSegment, DateTime date)
        {
            List<DateTime> result = new List<DateTime>();
            long currentDestinationID = GetDestinationIDForWayStation(rSegment.StartWayStationID);

            var firstWayStationID = (from w in cont.WayStations
                                     where w.DestinationID == currentDestinationID
                                     & w.WayStationOrderNumber == 1
                                     select w.WayStationID).First();

            int time = GetTimeForRoadSegment(firstWayStationID, rSegment.StartWayStationID, true);

            int dayOfWeekIDPrevious = (int)date.AddDays(-1).DayOfWeek;

            var startHoursPrevious = from d in cont.Destinations
                                     join s in cont.StartHours
                                     on d.DestinationID equals s.DestinationID
                                     where s.SheduleTypeID == dayOfWeekIDPrevious & s.StartHourValue >= (1440 - time)
                                     & d.DestinationID == currentDestinationID
                                     orderby s.StartHourValue
                                     select s.StartHourValue;

            foreach (int i in startHoursPrevious)
            {
                DateTime d = new DateTime(date.Year, date.Month, date.Day, 0, 0, 0).AddMinutes(i + time);
                result.Add(d);
            }

            int dayOfWeekIDPresent = (int)date.DayOfWeek;

            var startHoursPresent = from d in cont.Destinations
                                    join s in cont.StartHours
                                    on d.DestinationID equals s.DestinationID
                                    where s.SheduleTypeID == dayOfWeekIDPresent & s.StartHourValue <= (1440 - time)
                                    & d.DestinationID == currentDestinationID
                                    orderby s.StartHourValue
                                    select s.StartHourValue;

            foreach (int i in startHoursPresent)
            {
                DateTime d = new DateTime(date.Year, date.Month, date.Day, 0, 0, 0).AddMinutes(i + time);
                result.Add(d);
            }

            return result;
        }

        public static decimal GetPriceSettlements(long startID, long endID)
        {
            List<long> commonDest = DijkstraSearching.GetCommonDest(startID, endID);

            decimal price = (from w in cont.WayStations
                             join s in cont.Settlements
                             on w.SettlementID equals s.SettlementID
                             where w.DestinationID == commonDest.First() &
                             w.SettlementID == endID
                             select w.WayStationPriceTo).First();

            return price;
        }

        public static List<long> GetAllSettlementID()
        {
            var allSettlements = from s in cont.Settlements
                                 select s.SettlementID;

            return allSettlements.ToList();
        }

        public static List<long> GetAllNextSettlementsID(long settlementID)
        {
            List<long> destinationListID = GetAllDestination(settlementID);
            List<long> allNextSettlementsID = new List<long>();

            foreach (long destinationID in destinationListID)
            {
                long nextSettlementID = GetNextSettlementID(destinationID, settlementID);

                if (nextSettlementID != -1)
                {
                    allNextSettlementsID.Add(nextSettlementID);
                }
            }

            return allNextSettlementsID;
        }

        private static long GetNextSettlementID(long destinstionID, long settlementID)
        {
            int currentOrderNumber = GetCurrentOrderNumber(destinstionID, settlementID);

            var orderNumberList = from d in cont.Destinations
                                  join w in cont.WayStations
                                  on d.DestinationID equals w.DestinationID
                                  where d.DestinationID == destinstionID & w.WayStationOrderNumber > currentOrderNumber
                                  orderby w.WayStationOrderNumber
                                  select w.SettlementID;

            if (orderNumberList.ToList().Count > 0)
            {
                return orderNumberList.ToList().First();
            }
            else
            {
                return -1;
            }
        }

        public static int GetCurrentOrderNumber(long destinstionID, long settlementID)
        {
            var currentNumber = (from d in cont.Destinations
                                 join w in cont.WayStations
                                 on d.DestinationID equals w.DestinationID
                                 where d.DestinationID == destinstionID & w.SettlementID == settlementID
                                 select w.WayStationOrderNumber).First();

            return currentNumber;
        }

        public static TimeSpan GetTimeSettlement(long startSettID, long endSettID)
        {
            long destID = DijkstraSearching.GetCommonDest(startSettID, endSettID).First();

            long startWay = (from w in cont.WayStations
                             where w.SettlementID == startSettID &
                             w.DestinationID == destID
                             select w.WayStationID).First();

            long endWay = (from w in cont.WayStations
                           where w.SettlementID == endSettID &
                           w.DestinationID == destID
                           select w.WayStationID).First();

            int minutes = GetTimeForRoadSegment(startWay, endWay, false);

            TimeSpan result = new TimeSpan(0, minutes, 0);

            return result;
        }

        public static DateTime GetNextStartHour(DateTime arriveHour, long startSettlementID,
            long endSettlementID)
        {
            List<RoadSegment> rsList = GetRoadSegments(startSettlementID, endSettlementID);
            List<DateTime> startHours = new List<DateTime>();
            DateTime temp;
            DateTime min;

            temp = arriveHour;
            min = arriveHour.AddDays(8);

            foreach (RoadSegment rs in rsList)
            {
                while (startHours.Count == 0)
                {
                    foreach (DateTime d in GetStartHours(rs, temp))
                    {
                        if (d > arriveHour)
                        {
                            startHours.Add(d);
                            break;
                        }
                    }
                    temp = temp.AddDays(1);
                }

                foreach (DateTime d in startHours)
                {
                    if (d < min)
                    {
                        min = d;
                    }
                }
                startHours.Clear();
                temp = arriveHour;
            }

            return min;
        }

        public static List<WayStation> GetAllWayStationForSettlement(long settlementID)
        {
            var allWay = from w in cont.WayStations
                         where w.SettlementID == settlementID
                         select w;

            return allWay.ToList();
        }

        public static ComplexDestination CloneComplex(ComplexDestination compl)
        {
            ComplexDestination cloned = new ComplexDestination();

            foreach (long l in compl.settlementID)
            {
                cloned.settlementID.Add(l);
            }

            return cloned;
        }

        public static decimal GetPriceForRoadSegment(long destID, int startN, int endN)
        {
            decimal price = (from w in cont.WayStations
                             where w.DestinationID == destID
                             & w.WayStationOrderNumber > startN & w.WayStationOrderNumber <= endN
                             select w.WayStationPriceTo).Sum();

            return price;
        }

        #endregion
    }
}
